package com.acxy.ants.resful.doc;

import com.acxy.ants.common.kit.PathKit;
import com.acxy.ants.common.kit.StrKit;
import com.acxy.ants.resful.annotation.*;
import com.acxy.ants.resful.bean.Page;
import com.acxy.ants.resful.bind.LocalVariableTableParameterNameDiscoverer;
import com.alibaba.fastjson.JSON;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author MrShun
 * @version 1.0
 * @Date 2017-11-02
 */
public class ApiDocKit {

    private final static LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();

    public static void createDoc(String target, String[] urls, HttpServletRequest request, Method method, Object data) {
        ApiDoc apiDoc = new ApiDoc();
        GET get = method.getAnnotation(GET.class);
        POST post = method.getAnnotation(POST.class);
        String type = "", name = "", desc = "";
        if (get != null) {
            type = "GET";
            name = get.name();
            desc = StrKit.isBlank(get.desc())? null:get.desc();
        } else if (post != null) {
            type = "POST";
            name = post.desc();
            desc = StrKit.isBlank(post.desc())? null:post.desc();
        }

        apiDoc.setUrl(StrKit.join(urls, ","));
        apiDoc.setName(name);
        apiDoc.setDesc(desc);
        apiDoc.setType(type);
        apiDoc.setPath(target);

        List<Map<String, String>> headers = new ArrayList<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            Map map = new HashMap();
            String key = headerNames.nextElement();
            map.put(key, request.getHeader(key));
            headers.add(map);
        }

        apiDoc.setHeaders(headers);
        apiDoc.setCls(String.valueOf(method));
        List<RequestParams> requestParams = new ArrayList<>();
        Class<?>[] parameterTypes = method.getParameterTypes();
        String[] parameterNames = u.getParameterNames(method);
        Annotation[][] annotations = method.getParameterAnnotations();
        for (int i = 0; i < parameterTypes.length; i++) {
            if (parameterTypes[i] == HttpServletRequest.class ||
                    parameterTypes[i] == HttpServletResponse.class ||
                    parameterTypes[i] == HttpServletResponse.class) continue;

            Param param = null;
            if (annotations.length > 0) {
                for (int j = 0; j < annotations[i].length; j++) {
                    Annotation annotation = annotations[i][j];
                    if (annotation instanceof Param) param = (Param) annotation;
                }
            }
            Class<?> parameterType = parameterTypes[i];
            String parameterName = parameterNames[i];
            Entity entity = parameterType.getAnnotation(Entity.class);
            RequestParams requestParam = new RequestParams();
            requestParam.setName(parameterName);
            if (entity == null) {
                String t = parameterType.getSimpleName();
                if (parameterTypes[i] == Part.class || parameterTypes[i] == Part[].class)
                    requestParam.setType(t + "上传的文件对象");
                else
                    requestParam.setType(t + " " + toChineseType(t));
                setRequestParams(param, requestParam);
            } else {
                requestParam.setNotNull(false);
                requestParam.setType(parameterType.getSimpleName());
                requestParam.setRemarks(parameterType.getSimpleName() + "实体对象, 属性如下");
                requestParam.setErrorMsg("实体内校验");
            }
            requestParams.add(requestParam);

            if (entity != null) {
                Field[] fields = parameterType.getDeclaredFields();
                for (Field field : fields) {
                    RequestParams requestParam1 = new RequestParams();
                    requestParam1.setName(parameterName + "." + field.getName());
                    String t = field.getType().getSimpleName();
                    requestParam1.setType(t + " " + toChineseType(t));
                    Param param1 = field.getAnnotation(Param.class);
                    setRequestParams(param1, requestParam1);
                    requestParams.add(requestParam1);
                }
            }
        }
        apiDoc.setRequestParams(requestParams);
        Enumeration<String> params = request.getParameterNames();
        Map<String, String> mapParams = new HashMap();
        while (params.hasMoreElements()) {
            String key = params.nextElement();
            if (!"doc".equals(key))
                mapParams.put(key, request.getParameter(key));
        }
        apiDoc.setRequestStr(JSON.toJSONString(mapParams));

        List<ResponseParams> responseParams = new ArrayList<>();
        Class<?> dataCls = data.getClass();
        if (data instanceof Map) {
            Map m = (Map) data;
            Set<Map.Entry<String, Object>> set = m.entrySet();
            for (Map.Entry<String, Object> entry : set) {
                String key = entry.getKey();
                Object value = entry.getValue();
                ResponseParams responseParam = new ResponseParams();
                responseParam.setName(key);
                if (value != null) {
                    String t = value.getClass().getSimpleName();
                    responseParam.setType(t + " " + toChineseType(t));
                }
                if ("code".equals(key))
                    responseParam.setRemarks("响应吗");
                else if ("message".equals(key))
                    responseParam.setRemarks("消息描述");
                else if ("time".equals(key))
                    responseParam.setRemarks("时间戳");
                else if ("page".equals(key))
                    responseParam.setRemarks("当前页数");
                else if ("size".equals(key))
                    responseParam.setRemarks("每页大小");
                else if ("rows".equals(key))
                    responseParam.setRemarks("总记录数");
                else if ("total".equals(key))
                    responseParam.setRemarks("总页数");
                else if ("data".equals(key)) {
                    responseParam.setRemarks("数据对象");
                    responseParams.add(responseParam);
                    Object obj = m.get("data");
                    if (obj != null) {
                        if (obj instanceof Map) {
                            addMapParams(obj, responseParams);
                        } else if (obj instanceof List) {
                            List<Object> list = (List) obj;
                            for (Object o : list) {
                                if (o instanceof Map)
                                    addMapParams(o, responseParams);
                                else if (o instanceof List) {

                                } else {
                                    Class<?> dataCls2 = o.getClass();
                                    Entity entity = dataCls2.getAnnotation(Entity.class);
                                    if (entity != null) {
                                        addEntityParams(dataCls2, responseParams, " -  list.");
                                    }
                                }
                            }
                        } else {
                            Class<?> dataCls1 = obj.getClass();
                            Entity entity = dataCls1.getAnnotation(Entity.class);
                            if (entity != null) {
                                addEntityParams(dataCls1, responseParams, " - ");
                            }
                        }
                    }
                    continue;
                }
                responseParams.add(responseParam);
            }
        } else if (data instanceof List) {

        } else {
            Entity entity = dataCls.getAnnotation(Entity.class);
            if (entity != null) {
                addEntityParams(dataCls, responseParams, "");
            }
        }

        apiDoc.setResponseParams(responseParams);
        apiDoc.setResponseStr(JSON.toJSONString(data));
        makeFtl(request, apiDoc);
    }


    private static void makeFtl(HttpServletRequest request, ApiDoc apiDoc) {
        FileWriter out = null;
        try {
            // 通过FreeMarker的Confuguration读取相应的模板文件
            Configuration configuration = new Configuration();
            // 设置模板路径
            String path = PathKit.getClassPath(ApiDocKit.class);
            configuration.setDirectoryForTemplateLoading(new File(path + File.separator + "ftl"));
            // 设置默认字体
            configuration.setDefaultEncoding("utf-8");

            // 获取模板
            Template template = configuration.getTemplate("api.ftl");

            //设置输出文件
            String url = apiDoc.getPath().replace("/", File.separator);
            File f = new File(path + File.separator + "api" + url.substring(0, url.lastIndexOf(File.separator)));
            File file = null;
            if (!f.exists()) f.mkdirs();
            file = new File(path + File.separator + "api" + url + ".html");
            file.createNewFile();
            //设置输出流
            out = new FileWriter(file);
            //模板输出静态文件
            template.process(apiDoc, out);
            out.close();
        } catch (ParseException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TemplateException e) {
            e.printStackTrace();
        } finally {
            if (null != out) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void addMapParams(Object obj, List<ResponseParams> responseParams) {
        Set<Map.Entry<String, Object>> mm = ((Map) obj).entrySet();
        for (Map.Entry<String, Object> entry1 : mm) {
            ResponseParams responseParam = new ResponseParams();
            String key = entry1.getKey();
            Object value = entry1.getValue();
            if (value != null) {
                String t = value.getClass().getSimpleName();
                responseParam.setName(key);
                responseParam.setType(t + " " + toChineseType(t));
                responseParam.setRemarks("");
            }
            responseParams.add(responseParam);
        }
    }

    private static void addEntityParams(Class<?> dataCls, List<ResponseParams> responseParams, String p) {
        Field[] fields = dataCls.getDeclaredFields();
        for (Field field : fields) {
            Desc desc = field.getAnnotation(Desc.class);
            if(desc != null) {
                ResponseParams responseParam = new ResponseParams();
                responseParam.setName(p + field.getName());
                String t = field.getType().getSimpleName();
                responseParam.setType(t + " " + toChineseType(t));
                responseParam.setRemarks(desc.value());
                responseParams.add(responseParam);
            }
        }
    }

    private static void setRequestParams(Param param, RequestParams requestParam) {
        if (param == null) {
            requestParam.setNotNull(false);
            requestParam.setRemarks("");
            requestParam.setErrorMsg("");
        } else {
            requestParam.setNotNull(true);
            requestParam.setRemarks(param.remarks());
            requestParam.setErrorMsg(param.msg());
        }
    }

    //类型转中文提示
    private static String toChineseType(String type) {
        if ("String".equals(type))
            return "字符型";
        else if ("Integer".equals(type) || "int".equals(type))
            return "整型";
        else if ("Boolean".equals(type) || "boolean".equals(type))
            return "布尔型";
        else if ("Long".equals(type) || "long".equals(type))
            return "长整型";
        else if ("Byte".equals(type) || "byte".equals(type))
            return "字节型";
        else if ("Long".equals(type) || "long".equals(type))
            return "长整型";
        else if ("Long".equals(type) || "long".equals(type))
            return "长整型";
        else if ("Short".equals(type))
            return "短整型";
        else if ("Float".equals(type) || "float".equals(type))
            return "浮点型";
        else if ("Double".equals(type) || "double".equals(type))
            return "双精度浮点型";
        else if ("BigDecimal".equals(type))
            return "精确型";
        else if ("HashMap".equals(type) || "Map".equals(type))
            return "集合类型";
        else if ("ArrayList".equals(type) || "List".equals(type))
            return "数组类型";
        return "";
    }
}
